/*
 * @Author: sean
 * @Date:   2019-12-21 14:51:18
 * @Last Modified by:   sean
 * @Last Modified time: 2020-01-02 14:38:53
 */

const escodegen = require('escodegen');

const acorn = require('acorn');

class shriek {
    constructor(type, ...args) {
        this.type = type;
        switch (type) {
            case 'export':
                this.body = this.intExport(args[0]);
                break;
            case 'function':
                this.body = this.intFunction(args[0], args[1]);
                break;
            case 'method':
                this.body = this.intProperty(args[0], args[1], args[2]);
                break;
            case 'label':
                this.body = this.intLabel(args[0], args[1]);
                break;
            case 'object':
                this.body = this.initObject();
                break;
            case 'return':
                this.body = this.intReturn();
                break;
            case 'value':
                this.body = this.intValue(args[0]);
                break;
            case 'name':
                this.body = this.intName(args[0]);
                break;
            case 'this':
                this.body = this.intThis(args[0]);
                break;
            case 'chained':
                this.body = this.intChained(args[0], args[1]);
                break;
            case 'use': //匿名函数
                this.body = this.intUse(args[0], args[1]);
                break;
        }
        return this;
    }

    js() {
        return escodegen.generate(this.body);
    }

    ast() {
        return this.body;
    }

    toJs(ast) {
        return escodegen.generate(ast);
    }

    print() {
        console.log("print", to_json(this.body));
    }

    jsonToAst(object) {
        let value = "let ast = " + to_json(object);
        let data = acorn.parse(value);
        return data.body[0].declarations[0].init.properties;
    }

    append(data) {
        switch (this.type) {
            case 'export':
                this.body.body[0].declaration.properties.push(data);
                break;
            case 'method':
                if (this.body.method) {
                    this.body.value.body.body.push(data)
                } else {
                    this.body.value.properties.push(data)
                }
                break;
            case 'object':
                this.body.properties.push(data);
                break;
            case 'return':
                this.body.argument = data;
                break;
            case 'chained':
                this.body.arguments.push(data);
                break;
            case 'use':
                this.body.body.body.push(data);
                break;
            case 'function':
                this.body.value.body.body.push(data);
                break;
        }
        return this;
    }

    intReturn() {
        return {
            "type": "ReturnStatement",
            "argument": {}
        }
    }

    intExport() {
        return {
            "type": "Program",
            "body": [{
                "type": "ExportDefaultDeclaration",
                "declaration": {
                    "type": "ObjectExpression",
                    "properties": []
                }
            }],
            "sourceType": "module"
        }
    }

    /**
     * 设置匿名函数
     * @param  {[type]}  params   [description]
     * @param  {Boolean} is_async [description]
     * @return {[type]}           [description]
     */
    intUse(params, is_async = false) {
        let _params = [];
        if (is_array(params)) {
            for (let i in params) {
                let item = params[i];
                _params.push({
                    "type": "Identifier",
                    "name": item
                });
            }
        } else {
            _params.push({
                "type": "Identifier",
                "name": params
            });
        }
        return {
            "type": "ArrowFunctionExpression",
            "id": null,
            "expression": false,
            "generator": false,
            "async": is_async,
            "params": _params,
            "body": {
                "type": "BlockStatement",
                "body": []
            }
        }
    }

    /**
     * 设置函数
     * @param  {[type]}  name     [description]
     * @param  {Boolean} is_async [description]
     * @return {[type]}           [description]
     */
    intFunction(name, is_async = false) {
        return {
            "type": "Property",
            "method": true,
            "shorthand": false,
            "computed": false,
            "key": {
                "type": "Identifier",
                "name": name
            },
            "kind": "init",
            "value": {
                "type": "FunctionExpression",
                "id": null,
                "expression": false,
                "generator": false,
                "async": is_async,
                "params": [],
                "body": {
                    "type": "BlockStatement",
                    "body": []
                }
            }
        }
    }

    /**
     * 设置This.<name>
     * @param {[type]} name [description]
     */
    intThis(name) {
        return {
            "type": "MemberExpression",
            "object": {
                "type": "ThisExpression",
            },
            "property": {
                "type": "Identifier",
                "name": name
            },
            "computed": false
        }
    }

    /**
     * 设置链式
     * @param {[type]} name   名称
     * @param {[type]} object 上一级
     */
    intChained(name, object) {
        return {
            "type": "CallExpression",
            "callee": {
                "type": "MemberExpression",
                "object": object,
                "property": {
                    "type": "Identifier",
                    "name": name
                },
                "computed": false
            },
            "arguments": []
        }
    }

    intProperty(name, method = false, async = false) {
        let value = "";
        if (method) {
            let Block = this.BlockStatement();
            value = this.FunctionExpression(Block, async);
        } else {
            value = this.ObjectExpression();
        }
        return {
            "type": "Property",
            "method": method,
            "shorthand": false,
            "computed": false,
            "key": {
                "type": "Identifier",
                "name": name
            },
            "value": value,
            "kind": "init"
        }
    }

    intLabel(name, value) {
        return {
            "type": "Property",
            "method": false,
            "shorthand": false,
            "computed": false,
            "key": name,
            "value": value,
            "kind": "init"
        }
    }

    intValue(value) {
        return {
            "type": "Literal",
            "value": value
        }
    }

    intName(name) {
        return {
            "type": "Identifier",
            "name": name
        }
    }


    initObject() {
        return {
            "type": "ObjectExpression",
            "properties": []
        }
    }


    Property(name, value, method = false) {
        return {
            "type": "Property",
            "method": method,
            "shorthand": false,
            "computed": false,
            "key": {
                "type": "Identifier",
                "name": name
            },
            "value": value,
            "kind": "init"
        }
    }

    ObjectExpression() {
        return {
            "type": "ObjectExpression",
            "properties": []
        }
    }

    FunctionExpression(body, async = false, params = []) {
        return {
            "type": "FunctionExpression",
            "id": null,
            "expression": false,
            "generator": false,
            "async": async,
            "params": params,
            body: body
        }
    }

    BlockStatement() {
        return {
            type: "BlockStatement",
            body: []
        }
    }

    ReturnStatement(body) {
        return {
            "type": "ReturnStatement",
            "argument": {
                "type": "ObjectExpression",
                "properties": [body]
            }
        }
    }

    Literal(value) {
        return {
            "type": "Literal",
            "value": value,
        }
    }

    ArrayExpression(Array = []) {
        return {
            "type": "ArrayExpression",
            "elements": Array
        }
    }

}

module.exports = (type, ...args) => {
    return new shriek(type, ...args);
};